              package com.java.diagnostics.visualizer.impl.data;
              
              import com.java.diagnostics.visualizer.data.DataPoint;
              import com.java.diagnostics.visualizer.data.TupleData;
              import com.java.diagnostics.visualizer.data.TupleDataBuilder;
              import com.java.diagnostics.visualizer.data.axes.Axis;
              import com.java.diagnostics.visualizer.data.axes.AxisPair;
              import com.java.diagnostics.visualizer.data.axes.DataAxis;
              import com.java.diagnostics.visualizer.data.axes.XDataAxis;
              import com.java.diagnostics.visualizer.data.axes.YDataAxis;
              import com.java.diagnostics.visualizer.data.ids.ID;
              import com.java.diagnostics.visualizer.impl.axes.XDataAxisImpl;
              import com.java.diagnostics.visualizer.impl.data.ids.TupleIDImpl;
              import com.java.diagnostics.visualizer.metadata.TupleMetaData;
              import com.java.diagnostics.visualizer.properties.OutputProperties;

              import java.util.ArrayList;
              import java.util.Collections;
              import java.util.Comparator;
              import java.util.HashMap;
              import java.util.Iterator;
              import java.util.List;
              import java.util.Map;
              import java.util.PriorityQueue;
              
              public class TupleDataImpl extends DomainDataImpl
                implements TupleDataBuilder
              {
                protected AxisPair axisPair;
                private XDataAxisImpl xAxis;
                private DataPoint minX = null;
              
                private DataPoint minY = null;
              
                private DataPoint maxX = null;
              
                private DataPoint maxY = null;
              
                private double totalRawX = 0.0D;
              
                private double totalRawY = 0.0D;
                private List<DataPoint> dataPointList;
                private int lastSequence;
                private DataPoint[] dataPointArray;
                private static final DataPoint[] DATA_POINT_TEMPLATE_ARRAY = new DataPoint[0];
                private Map<String, Double> unitToTotalX;
                private Map<String, Double> unitToMinX;
                private Map<String, Double> unitToMaxX;
                private Map<String, Double> unitToTotalY;
                private Map<String, Double> unitToMinY;
                private Map<String, Double> unitToMaxY;
                private double lastNormalisationOffset = -1.0D;
              
                private Object convertedValuesLock = new Object();
              
                protected TupleMetaData metaData = null;
              
                public TupleDataImpl(String label, String displayName, String category, AxisPair axisPair, DataPoint[] initialDataPoints)
                {
                  this(label, displayName, category, axisPair);
              
                  setDataPoints(initialDataPoints);
                }
              
                public TupleDataImpl(String label, String displayName, String category, AxisPair axisPair)
                {
                  super(label, displayName, category);
                  this.axisPair = axisPair;
                  this.xAxis = ((XDataAxisImpl)axisPair.getXAxis());
                  this.lastSequence = -1;
                }
              
                public double getRawMaxX()
                {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.maxX.getRawX();
                }
              
                public double getRawMaxY() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.maxY.getRawY();
                }
              
                public double getRawMinX() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.minX.getRawX();
                }
              
                public double getRawMinY() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.minY.getRawY();
                }
              
                public double getRawTotalX() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.totalRawX;
                }
              
                public double getRawTotalY() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.totalRawY;
                }
              
                public double getRawMeanX() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.totalRawX / this.dataPointList.size();
                }
              
                public double getRawMeanY() {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return this.totalRawY / this.dataPointList.size();
                }
              
                public final DataPoint[] getDataPoints() {
                  if (this.dataPointList == null) {
                    return DATA_POINT_TEMPLATE_ARRAY;
                  }
                  if (this.dataPointArray == null) {
                    this.dataPointArray = ((DataPoint[])this.dataPointList.toArray(DATA_POINT_TEMPLATE_ARRAY));
                  }
                  return this.dataPointArray;
                }
              
                public PriorityQueue<DataPoint> getSortedDataPoints() {
                  PriorityQueue points = new PriorityQueue();
                  for (DataPoint point : getDataPoints()) {
                    points.add(point);
                  }
                  return points;
                }
              
                public DataPoint getLastDataPoint()
                {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return null;
                  }
                  DataPoint dataPoint = (DataPoint)this.dataPointList.get(this.dataPointList.size() - 1);
                  return dataPoint;
                }
              
                public DataPoint getDataPoint(int sequenceUID)
                {
                  if (this.dataPointList == null) {
                    return null;
                  }
                  DataPoint dataPoint = null;
              
                  int index = Collections.binarySearch(this.dataPointList, new DataPointImpl(
                    sequenceUID, 0.0D, 0.0D, null, this.axisPair), 
                    new Comparator() {
                    public int compare(DataPoint o1, DataPoint o2) {
                      return o1.getSequenceUID() - o2.getSequenceUID();
                    }
             

@Override
public int compare(Object o1, Object o2) {
	// TODO Auto-generated method stub
	return 0;
}     });
                  if (index >= 0) {
                    dataPoint = (DataPoint)this.dataPointList.get(index);
                  }
              
                  return dataPoint;
                }
              
                public XDataAxis getXAxis() {
                  return this.axisPair.getXAxis();
                }
              
                public YDataAxis getYAxis() {
                  return this.axisPair.getYAxis();
                }
              
                protected ID generateID() {
                  return new TupleIDImpl(getCategory(), this.label);
                }
              
                public final void addDataPoint(double y, String comment)
                {
                  this.lastSequence = this.xAxis.checkSequenceUID(this.lastSequence);
                  DataPoint dataPoint = new DataPointImpl(this.xAxis.getSequenceUID(), 
                    this.xAxis.getX(), y, comment, this.axisPair);
                  addDataPoint(dataPoint);
                }
              
                /** @deprecated */
                public final void addDataPoint(int sequenceNumber, double x, double y, String comment)
                {
                  this.lastSequence = this.xAxis.checkSequenceUID(this.lastSequence);
                  DataPoint dataPoint = new DataPointImpl(sequenceNumber, x, y, comment, 
                    this.axisPair);
                  addDataPoint(dataPoint);
                }
              
                public final void addDataPoint(DataPoint dataPoint)
                {
                  if (this.dataPointList == null) {
                    this.dataPointList = new ArrayList();
                  }
              
                  this.dataPointList.add(dataPoint);
                  this.dataPointArray = null;
                  if ((this.minX == null) || (dataPoint.getRawX() < this.minX.getRawX())) {
                    this.minX = dataPoint;
                  }
                  if ((this.maxX == null) || (dataPoint.getRawX() > this.maxX.getRawX())) {
                    this.maxX = dataPoint;
                  }
                  if ((this.minY == null) || (dataPoint.getRawY() < this.minY.getRawY())) {
                    this.minY = dataPoint;
                  }
                  if ((this.maxY == null) || (dataPoint.getRawY() > this.maxY.getRawY())) {
                    this.maxY = dataPoint;
                  }
                  this.totalRawX += dataPoint.getRawX();
                  this.totalRawY += dataPoint.getRawY();
                  clearConvertedMinMaxAndTotals();
                }
              
                public final void setDataPoints(DataPoint[] newDataPoints)
                {
                  this.dataPointList = null;
                  this.minX = null;
                  this.minY = null;
                  this.maxX = null;
                  this.maxY = null;
                  this.totalRawX = 0.0D;
                  this.totalRawY = 0.0D;
                  this.dataPointArray = null;
                  clearConvertedMinMaxAndTotals();
                  if (newDataPoints == null) {
                    return;
                  }
                  for (DataPoint dp : newDataPoints)
                  {
                    addDataPoint(dp);
                  }
                }
              
                public boolean isEmpty()
                {
                  return (this.dataPointList == null) || (this.dataPointList.isEmpty());
                }
              
                public AxisPair getAxisPair()
                {
                  return this.axisPair;
                }
              
                public TupleData getCroppedTupleData(OutputProperties properties)
                {
                  if (!tupleIsCropped(properties))
                  {
                    return this;
                  }
              
                  DataPoint[] masterDataPoints = getDataPoints();
              
                  DataAxis xAxis = getAxisPair().getXAxis();
                  DataAxis yAxis = getAxisPair().getYAxis();
              
                  String xBaseUnits = xAxis.getAxis().getBaseUnitName();
                  String yBaseUnits = yAxis.getAxis().getBaseUnitName();
              
                  Double minX = properties.getMinimumX(xBaseUnits);
                  Double maxX = properties.getMaximumX(xBaseUnits);
                  Double minY = properties.getMinimumY(yBaseUnits);
                  Double maxY = properties.getMaximumY(yBaseUnits);
              
                  double minXVal = -1.797693134862316D+308D;
                  double minYVal = -1.797693134862316D+308D;
                  double maxXVal = 1.7976931348623157E+308D;
                  double maxYVal = 1.7976931348623157E+308D;
              
                  if (minX != null) {
                    minXVal = minX.doubleValue();
                  }
                  if (minY != null) {
                    minYVal = minY.doubleValue();
                  }
                  if (maxX != null)
                  {
                    maxXVal = maxX.doubleValue();
                  }
                  if (maxY != null) {
                    maxYVal = maxY.doubleValue();
                  }
              
                  List newPoints = new ArrayList();
                  String xUnits = properties.getXUnits(this);
                  String yUnits = properties.getYUnits(this);
                  for (int j = 0; j < masterDataPoints.length; j++) {
                    DataPoint dataPoint = masterDataPoints[j];
                    double xval = dataPoint.getX(xUnits);
                    double yval = dataPoint.getY(yUnits);
                    if ((xval >= minXVal) && (xval <= maxXVal) && 
                      (yval >= minYVal) && (yval <= maxYVal)) {
                      newPoints.add(dataPoint);
                    }
              
                  }
              
                  DataPoint[] dataPoints = 
                    (DataPoint[])newPoints
                    .toArray(new DataPoint[0]);
              
                  TupleDataBuilder croppedTuple = new TupleDataImpl(getLabel(), 
                    getDisplayName(), getCategory(), getAxisPair(), dataPoints);
                  croppedTuple.setTupleMetaData(getTupleMetaData());
                  return croppedTuple;
                }
              
                public boolean tupleIsCropped(OutputProperties properties)
                {
                  DataAxis xAxis = getAxisPair().getXAxis();
                  DataAxis yAxis = getAxisPair().getYAxis();
              
                  String xBaseUnits = xAxis.getAxis().getBaseUnitName();
                  String yBaseUnits = yAxis.getAxis().getBaseUnitName();
              
                  Double minX = properties.getMinimumX(xBaseUnits);
                  Double maxX = properties.getMaximumX(xBaseUnits);
                  Double minY = properties.getMinimumY(yBaseUnits);
                  Double maxY = properties.getMaximumY(yBaseUnits);
              
                  if ((minX != null) || (maxX != null) || (minY != null) || (maxY != null)) {
                    return true;
                  }
                  return false;
                }
              
                public int length()
                {
                  if (this.dataPointList == null) {
                    return 0;
                  }
                  return this.dataPointList.size();
                }
              
                public double getMaxX(String units)
                {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
              
                  double maxX = 0.0D;
                  synchronized (this.convertedValuesLock) {
                    if ((this.unitToMaxX == null) || 
                      (this.unitToMaxX.get(units) == null) || 
                      (this.xAxis.getNormalisationOffset() != this.lastNormalisationOffset)) {
                      populateConvertedMinMaxAndTotals();
                    }
                    Double maxXDouble = (Double)this.unitToMaxX.get(units);
                    if (maxXDouble != null) {
                      maxX = maxXDouble.doubleValue();
                    }
                  }
                  return maxX;
                }
              
                public double getMaxY(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
              
                  double maxY = 0.0D;
                  synchronized (this.convertedValuesLock) {
                    if ((this.unitToMaxY == null) || 
                      (this.unitToMaxY.get(units) == null) || 
                      (this.xAxis.getNormalisationOffset() != this.lastNormalisationOffset)) {
                      populateConvertedMinMaxAndTotals();
                    }
                    Double maxYDouble = (Double)this.unitToMaxY.get(units);
                    if (maxYDouble != null) {
                      maxY = maxYDouble.doubleValue();
                    }
                  }
                  return maxY;
                }
              
                public double getMinX(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
              
                  double minX = 0.0D;
                  synchronized (this.convertedValuesLock) {
                    if ((this.unitToMinX == null) || 
                      (this.unitToMinX.get(units) == null) || 
                      (this.xAxis.getNormalisationOffset() != this.lastNormalisationOffset)) {
                      populateConvertedMinMaxAndTotals();
                    }
                    Double minXDouble = (Double)this.unitToMinX.get(units);
                    if (minXDouble != null) {
                      minX = minXDouble.doubleValue();
                    }
                  }
                  return minX;
                }
              
                public double getMinY(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
              
                  double minY = 0.0D;
                  synchronized (this.convertedValuesLock) {
                    if ((this.unitToMinY == null) || 
                      (this.unitToMinY.get(units) == null) || 
                      (this.xAxis.getNormalisationOffset() != this.lastNormalisationOffset)) {
                      populateConvertedMinMaxAndTotals();
                    }
                    Double minYDouble = (Double)this.unitToMinY.get(units);
                    if (minYDouble != null) {
                      minY = minYDouble.doubleValue();
                    }
                  }
                  return minY;
                }
              
                public double getTotalX(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
              
                  double totalX = 0.0D;
                  synchronized (this.convertedValuesLock) {
                    if ((this.unitToTotalX == null) || 
                      (this.unitToTotalX.get(units) == null) || 
                      (this.xAxis.getNormalisationOffset() != this.lastNormalisationOffset)) {
                      populateConvertedMinMaxAndTotals();
                    }
                    Double totalXDouble = (Double)this.unitToTotalX.get(units);
                    if (totalXDouble != null) {
                      totalX = totalXDouble.doubleValue();
                    }
                  }
                  return totalX;
                }
              
                public double getTotalY(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
              
                  double totalY = 0.0D;
                  synchronized (this.convertedValuesLock) {
                    if ((this.unitToTotalY == null) || 
                      (this.unitToTotalY.get(units) == null) || 
                      (this.xAxis.getNormalisationOffset() != this.lastNormalisationOffset)) {
                      populateConvertedMinMaxAndTotals();
                    }
                    Double totalYDouble = (Double)this.unitToTotalY.get(units);
                    if (totalYDouble != null) {
                      totalY = totalYDouble.doubleValue();
                    }
                  }
                  return totalY;
                }
              
                public double getMeanX(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return getTotalX(units) / length();
                }
              
                public double getMeanY(String units) {
                  if ((this.dataPointList == null) || (this.dataPointList.size() == 0)) {
                    return 0.0D;
                  }
                  return getTotalY(units) / length();
                }
              
                private void populateConvertedMinMaxAndTotals()
                {
                  synchronized (this.convertedValuesLock)
                  {
                    this.unitToTotalX = new HashMap();
                    this.unitToMinX = new HashMap();
                    this.unitToMaxX = new HashMap();
                    this.unitToTotalY = new HashMap();
                    this.unitToMinY = new HashMap();
                    this.unitToMaxY = new HashMap();
              
                    this.lastNormalisationOffset = this.xAxis.getNormalisationOffset();
              
                    String[] allXUnits = getXAxis().getPossibleUnits();
                    String[] allYUnits = getYAxis().getPossibleUnits();
              
                    for (String xUnit : allXUnits) {
                      this.unitToTotalX.put(xUnit, Double.valueOf(0.0D));
                      this.unitToMinX.put(xUnit, Double.valueOf(1.7976931348623157E+308D));
                      this.unitToMaxX.put(xUnit, Double.valueOf(-1.797693134862316D+308D));
                    }
                    for (String yUnit : allYUnits) {
                      this.unitToTotalY.put(yUnit, Double.valueOf(0.0D));
                      this.unitToMinY.put(yUnit, Double.valueOf(1.7976931348623157E+308D));
                      this.unitToMaxY.put(yUnit, Double.valueOf(-1.797693134862316D+308D));
                    }
                    // edited by wenguang.huang 
                    for (Iterator localIterator = this.dataPointList.iterator(); localIterator.hasNext();) 
                      
                    {
                      DataPoint p = (DataPoint)localIterator.next();
                      for (String xUnit : allXUnits) {
                        double xValue = p.getX(xUnit);
                        this.unitToTotalX.put(xUnit, Double.valueOf(((Double)this.unitToTotalX.get(xUnit)).doubleValue() + xValue));
                        this.unitToMinX.put(xUnit, 
                          Double.valueOf(Math.min(((Double)this.unitToMinX.get(xUnit)).doubleValue(), 
                          xValue)));
                        this.unitToMaxX.put(xUnit, 
                          Double.valueOf(Math.max(((Double)this.unitToMaxX.get(xUnit)).doubleValue(), 
                          xValue)));
                      }
                     for (String yUnit : allYUnits) {
                  
                      double yValue = p.getY(yUnit);
                      this.unitToTotalY.put(yUnit, Double.valueOf(((Double)this.unitToTotalY.get(yUnit)).doubleValue() + yValue));
                      this.unitToMinY.put(yUnit, 
                        Double.valueOf(Math.min(((Double)this.unitToMinY.get(yUnit)).doubleValue(), 
                        yValue)));
                      this.unitToMaxY.put(yUnit, 
                        Double.valueOf(Math.max(((Double)this.unitToMaxY.get(yUnit)).doubleValue(), 
                        yValue)));
              
                      }
                    }
                  }
                }
              
                private void clearConvertedMinMaxAndTotals()
                {
                  synchronized (this.convertedValuesLock) {
                    this.unitToTotalX = null;
                    this.unitToMinX = null;
                    this.unitToMaxX = null;
                    this.unitToTotalY = null;
                    this.unitToMinY = null;
                    this.unitToMaxY = null;
                  }
                }
              
                public TupleMetaData getTupleMetaData() {
                  return this.metaData;
                }
              
                public void setTupleMetaData(TupleMetaData metaData)
                {
                  this.metaData = metaData;
                }
              }
